package com.xyt.imei.core.service.impl;

import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong;

import org.apache.poi.ss.usermodel.Workbook;
import org.jeecgframework.poi.excel.ExcelExportUtil;
import org.jeecgframework.poi.excel.entity.ExportParams;
import org.jeecgframework.poi.excel.entity.params.ExcelExportEntity;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import com.luminary.common.framework.core.exception.SysException;
import com.luminary.common.framework.core.model.Pagination;
import com.luminary.common.framework.dao.GenericDao;
import com.luminary.common.framework.service.impl.GenericServiceImpl;
import com.xyt.imei.core.dao.StoreDao;
import com.xyt.imei.core.dao.TraceDao;
import com.xyt.imei.core.dao.UserDao;
import com.xyt.imei.core.meta.RoleEnum;
import com.xyt.imei.core.meta.StoreStatusEnum;
import com.xyt.imei.core.meta.TraceEventEnum;
import com.xyt.imei.core.model.converter.StoreConverter;
import com.xyt.imei.core.model.dto.WaitReciveUndoDTO;
import com.xyt.imei.core.model.po.Store;
import com.xyt.imei.core.model.po.Trace;
import com.xyt.imei.core.model.po.User;
import com.xyt.imei.core.model.vo.StoreVO;
import com.xyt.imei.core.service.StoreService;
import com.xyt.imei.core.service.TraceService;
import com.xyt.imei.core.service.UserService;

import lombok.Data;

@Service
public class StoreServiceImpl extends GenericServiceImpl<Store, String> implements StoreService {

	@Autowired
	private StoreConverter storeConverter;
	
	@Autowired
	private UserService userService;
	
	@Autowired
	private TraceService traceService;
	
	@Autowired
	private TraceDao traceDao;
	
	@Autowired
	private StoreDao storeDao;
	
	@Autowired
	private UserDao userDao;
	
	@Override
	protected GenericDao<Store, String> getGenericDao() {
		return this.storeDao;
	}

	@Override
	public StoreVO findByImeiNo(String imeiNo) throws Exception {
		
		Store store = findByPrimaryKey(imeiNo);
		StoreVO storeVO = storeConverter.po2vo(store);
		return storeVO;
		
	}
	
	@Override
	public Pagination<StoreVO> findWaitRecivePageBySource(Pagination<Store> page) throws Exception {
		
		Pagination<StoreVO> pageination = new Pagination<StoreVO>();
		
		Map<String, Object> params = page.getParams();
		long totalRecords = storeDao.selectRecordCountWaitReciveSourceByPage(params);
		
		page.setTotalRecords((int) totalRecords);// 分页总记录数
		page.setTotalPages(page.countTotalPages());// 分页总页数
		page.setCurrentPage(page.countCurrentPage());// 当前页
		page.setStartRow(page.countStartRow());// 当前记录起始索引
		
		params.put(Pagination.START_ROW_NAME, page.getStartRow());
        params.put(Pagination.PAGE_SIZE_NAME, page.getPageSize());
		
		List<Store> storeList = storeDao.selectWaitRecivePageBySource(params);
		List<StoreVO> vos = storeConverter.po2vo(storeList);
		pageination.setResults(vos);
		return pageination;
		
	}

	@Override
	public Pagination<StoreVO> findWaitRecivePageByTarget(Pagination<Store> page) throws Exception {
		
		Pagination<StoreVO> pageination = new Pagination<StoreVO>();
		
		Map<String, Object> params = page.getParams();
		long totalRecords = storeDao.selectRecordCountWaitReciveTargetByPage(params);
		
		page.setTotalRecords((int) totalRecords);// 分页总记录数
		page.setTotalPages(page.countTotalPages());// 分页总页数
		page.setCurrentPage(page.countCurrentPage());// 当前页
		page.setStartRow(page.countStartRow());// 当前记录起始索引
		
		params.put(Pagination.START_ROW_NAME, page.getStartRow());
        params.put(Pagination.PAGE_SIZE_NAME, page.getPageSize());
        
        BeanUtils.copyProperties(page, pageination);
		
        List<Store> storeList = storeDao.selectWaitRecivePageByTarget(params);
        List<StoreVO> vos = storeConverter.po2vo(storeList);
		pageination.setResults(vos);
		return pageination;
		
	}
	
	@Override
	@Transactional
	public boolean store(Store store, User user) throws Exception {
		
		User branch = userService.findByPrimaryKey(store.getUserId());
		if(branch == null) {
			throw new SysException("500", "网点不存在");
		} else if(branch.getRole() != RoleEnum.BRANCH_VALUE){
			throw new SysException("500", "入库对象不是网点");
		}
		
		Trace trace = new Trace();
		trace.setImeiNo(store.getImeiNo());
		trace.setEvent(TraceEventEnum.STORE.getValue());
		trace.setSourceUserId(user.getUserId());
		trace.setTargetUserId(branch.getUserId());
		trace.setCreateBy(user.getUserId());
		trace.setCreateDate(new Date());
		
		if(!traceService.addTrace(trace))
			throw new SysException("500", "新增串码轨迹失败");
		
		return addSelective(store) == 1;
		
	}

	@Override
	public Pagination<StoreVO> findPageBybranch(Pagination<Store> page) throws Exception {
		
		User user = userService.findByPrimaryKey((Integer) page.getParams().get("userId"));
		
		Pagination<StoreVO> pageination = new Pagination<StoreVO>();
		int rows = storeDao.selectCountByBranch(page.getParams());
		page.setTotalRecords(rows);
		page.setTotalPages(page.countTotalPages());
		
		BeanUtils.copyProperties(page, pageination);
		
		List<Store> list = storeDao.selectPageByBranch(page.getParams());
		List<StoreVO> vos = storeConverter.po2vo(list, user.getUsername());
		
		pageination.setResults(vos);
		
		return pageination;
		
	}

	@Override
	@Transactional
	public boolean undo(List<WaitReciveUndoDTO> waitReciveUndoDTO, User user) throws Exception {
		
		List<Store> storeList = new ArrayList<Store>();
		for(WaitReciveUndoDTO dto : waitReciveUndoDTO) {
			Store store = findByPrimaryKey(dto.getImeiNo());
			
			if(store == null) 
				throw new SysException("500", "串号不存在" + dto.getImeiNo());
			else if(store.getUserId() != user.getUserId()) 
				throw new SysException("500", "库存中没有此串号" + dto.getImeiNo());
			else if(store.getStatus() != StoreStatusEnum.WAIT_RECIVE.getValue())
				throw new SysException("500", "串号不是待接收状态" + dto.getImeiNo());
			  
			store.setUpdateBy(user.getUserId());
			store.setUpdateDate(new Date());
			
			storeList.add(store);
		}
		
		// 乐观更新
		return storeDao.updateNormalStatusByBatch(storeList) == storeList.size();
		
	}

	@Override
	@Transactional
	public boolean recive(List<WaitReciveUndoDTO> waitReciveUndoDTO, User user) throws Exception {
		
		List<Store> storeList = new ArrayList<Store>();
		List<Trace> traceList = new ArrayList<Trace>();
		
		for(WaitReciveUndoDTO dto : waitReciveUndoDTO) {
			Store store = findByPrimaryKey(dto.getImeiNo());
			
			if(store == null) 
				throw new SysException("500", "串号不存在" + dto.getImeiNo());
			else if(store.getStatus() != StoreStatusEnum.WAIT_RECIVE.getValue())
				throw new SysException("500", "串号不是待接收状态" + dto.getImeiNo());
			else if(store.getTargetUserId() != user.getUserId()) 
				throw new SysException("500", "串号接收网点不匹配" + dto.getImeiNo());
			
			store.setUpdateBy(user.getUserId());
			store.setUpdateDate(new Date());
			
			storeList.add(store);
			
			Trace trace = new Trace();
			trace.setImeiNo(dto.getImeiNo());
			trace.setEvent(TraceEventEnum.ALLOT.getValue());
			trace.setSourceUserId(store.getUserId());
			trace.setTargetUserId(store.getTargetUserId());
			trace.setCreateBy(user.getUserId());
			trace.setCreateDate(new Date());
			traceList.add(trace);
			
		}
		
		boolean isSuccess = traceDao.insertByBatch(traceList) == traceList.size();
		if(!isSuccess)
			throw new SysException("500", "跟踪记录新增失败");
		
		// 乐观更新
		return storeDao.updateReciveStatusByBatch(storeList) == storeList.size();
		
	}

	@Override
	@Transactional
	public boolean store(List<Store> store, List<Trace> trace, User user) throws Exception {
		boolean success = storeDao.insertByBatch(store) == store.size();
		if(!success)
			throw new SysException("500", "入库失败");
		
		return traceDao.insertByBatch(trace) == trace.size();
	}

	@Override
	public Workbook export() throws Exception {
		List<User> branchs = userDao.selectAllBranch();
		Workbook workbook = null;
		
		for(User branch : branchs) {
			Map<String, Object> params = new HashMap<String, Object>();
			params.put("userId", branch.getUserId());
			params.put("status", 0);
			long count = storeDao.selectCountByBranch(params);
			if(count > 10000) {
				long rest = 0;
				long parts = count % 10000 == 0 ? count / 10000 : count / 10000 +1;
				for(int i = 0; i < parts - 1; i++) {
					params.put("startRow", 10000 * i);
					params.put("pageSize", 10000);
					
					List<Store> store = storeDao.selectPageByBranch(params);
					List<StoreVO> vos = storeConverter.po2vo(store, branch.getUsername(), false);
					try {
						workbook = ExcelExportUtil.exportBigExcel(new ExportParams(null, "Sheet1"), StoreVO.class, vos);
					} catch (Exception e) {
						e.printStackTrace();
						//ExcelExportUtil.closeExportBigExcel();
						return null;
					} 
					rest = count - 10000;
				}
				
				params.put("startRow", 10000 * (parts - 1));
				params.put("pageSize", rest);
				
				List<Store> store = storeDao.selectPageByBranch(params);
				List<StoreVO> vos = storeConverter.po2vo(store, branch.getUsername(), false);
				
				try {
					workbook = ExcelExportUtil.exportBigExcel(new ExportParams(null, "Sheet1"), StoreVO.class, vos);
				} catch (Exception e) {
					e.printStackTrace();
					//ExcelExportUtil.closeExportBigExcel();
					return null;
				} 
			} else if(count > 0) {
				List<Map<String, Object>> list = new ArrayList<Map<String, Object>>();
				params.put("startRow", 0);
				params.put("pageSize", count);

				List<Store> store = storeDao.selectPageByBranch(params);
				List<StoreVO> vos = storeConverter.po2vo(store, branch.getUsername(), false);
				
				try {
					workbook = ExcelExportUtil.exportBigExcel(new ExportParams(null, "Sheet1"), StoreVO.class, vos);
				} catch (Exception e) {
					e.printStackTrace();
					//ExcelExportUtil.closeExportBigExcel();
					return null;
				} 
			}
			
		}
		
		Optional
		.ofNullable(workbook)
		.ifPresent(w->{
			//ExcelExportUtil.closeExportBigExcel();
		});
		
		return workbook;
	}

}
